feat: migrate experiments config from array to bools#398
feat: migrate experiments config from array to bools#398
Conversation
Change the experiments field in SystemConfig, ProjectConfig, and Config
from []experiment.Experiment to map[string]bool. This enables dot-notation
access for the upcoming config command and allows explicit enable/disable
of experiments.
Includes backwards-compatible JSON unmarshaling that auto-converts the old
array format (["charm"]) to the new map format ({"charm": true}).
Updates help output to show all available experiments with ENABLED/DISABLED
status for better discoverability.
Reverts the EXPERIMENTS section changes from the experiments migration commit to keep PR #1 focused on the array-to-map refactor. Help menu improvements will be done in a separate PR.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #398 +/- ##
=======================================
Coverage ? 67.91%
=======================================
Files ? 219
Lines ? 18103
Branches ? 0
=======================================
Hits ? 12294
Misses ? 4649
Partials ? 1160 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
zimeg
left a comment
There was a problem hiding this comment.
@mwbrooks LGTM! This is an amazing change for improved configurations overall - too often I changed "bolt" to "bolts" for confident testing ⚡ 🏁
I'm leaving a few questions related to the order of detecting experiments as well as question about typings. I also don't think we should preserve backward compatibilities for experiments since that adds meaningful logic otherwise but no blockers 🎆
|
|
||
| // Feature experiments | ||
| ExperimentsFlag []string | ||
| experiments []experiment.Experiment |
There was a problem hiding this comment.
🪬 question: Can we continue to use strict types of experiment or is this not allowed as a key?
experiments map[experiment.Experiment]boolThere was a problem hiding this comment.
@zimeg Great suggestion and I should have caught that myself. I think we can use map[experiment.Experiment]bool, so let me look into refactoring it 💻
| // Load from flags | ||
| for _, flagValue := range c.ExperimentsFlag { | ||
| experiments = append(experiments, experiment.Experiment(flagValue)) | ||
| experiments[flagValue] = true | ||
| } |
There was a problem hiding this comment.
👁️🗨️ thought: The order of experiment parsing might need to be reversed so that project and user configurations can be overridden with a flag if the lower settings use a "false" value?
| jsonContents := []byte(fmt.Sprintf("{\"last_update_checked_at\":\"2023-05-11T15:41:07.799619-07:00\",\"experiments\":[\"%s\"]}", validExperiment)) | ||
| jsonContents := []byte(fmt.Sprintf(`{"last_update_checked_at":"2023-05-11T15:41:07.799619-07:00","experiments":{"%s":true}}`, validExperiment)) |
There was a problem hiding this comment.
⭐ praise: Thanks for improving both the string formatting and experiments formatting here!
| t.Run("Correctly finds experiments from old array format in config.json", func(t *testing.T) { | ||
| // Setup | ||
| ctx, fs, _, config, pathToConfigJSON, teardown := setup(t) | ||
| defer teardown(t) | ||
| _, mockPrintDebug := setupMockPrintDebug() | ||
|
|
||
| // Write old array format | ||
| jsonContents := []byte( | ||
| fmt.Sprintf( | ||
| `{"last_update_checked_at":"2023-05-11T15:41:07.799619-07:00","experiments":["%s"]}`, | ||
| validExperiment, | ||
| ), | ||
| ) | ||
| _ = afero.WriteFile(fs, pathToConfigJSON, jsonContents, 0600) | ||
|
|
||
| config.LoadExperiments(ctx, mockPrintDebug) | ||
| experimentOn := config.WithExperimentOn(validExperiment) | ||
| assert.Equal(t, true, experimentOn) | ||
| }) | ||
|
|
There was a problem hiding this comment.
| t.Run("Correctly finds experiments from old array format in config.json", func(t *testing.T) { | |
| // Setup | |
| ctx, fs, _, config, pathToConfigJSON, teardown := setup(t) | |
| defer teardown(t) | |
| _, mockPrintDebug := setupMockPrintDebug() | |
| // Write old array format | |
| jsonContents := []byte( | |
| fmt.Sprintf( | |
| `{"last_update_checked_at":"2023-05-11T15:41:07.799619-07:00","experiments":["%s"]}`, | |
| validExperiment, | |
| ), | |
| ) | |
| _ = afero.WriteFile(fs, pathToConfigJSON, jsonContents, 0600) | |
| config.LoadExperiments(ctx, mockPrintDebug) | |
| experimentOn := config.WithExperimentOn(validExperiment) | |
| assert.Equal(t, true, experimentOn) | |
| }) |
🪓 quibble(non-blocking): We might not fear breaking experimental configuration setups since this can become burdensome to keep in mind ongoing?
mwbrooks
left a comment
There was a problem hiding this comment.
Whoops, like a dummy I forgot to publish my comments for the reviewers 🤦🏻
|
|
||
| // Write our test script to the memory file system used by the mock | ||
| jsonContents := []byte(fmt.Sprintf("{\"last_update_checked_at\":\"2023-05-11T15:41:07.799619-07:00\",\"experiments\":[\"%s\"]}", validExperiment)) | ||
| jsonContents := []byte(fmt.Sprintf(`{"last_update_checked_at":"2023-05-11T15:41:07.799619-07:00","experiments":{"%s":true}}`, validExperiment)) |
There was a problem hiding this comment.
note: Just updating the formatting to make the tests more readable with literals and no escapes.
| assert.Contains(t, mockOutput.String(), "active project experiments: []") | ||
| assert.Contains(t, mockOutput.String(), fmt.Sprintf("active permanently enabled experiments: [%s]", experiment.Placeholder)) | ||
| assert.Equal(t, []experiment.Experiment{experiment.Placeholder}, config.experiments) | ||
| assert.ElementsMatch(t, []experiment.Experiment{experiment.Placeholder}, config.GetExperiments()) |
There was a problem hiding this comment.
note: Ordering now varies because it's a map instead of slice.
Changelog
Summary
This pull request migrates the experiments config format from an array to a map of boolean values. It includes backwards-compatible JSON unmarshaling that will auto-migrate
config.jsonfiles.Example
Before:
{ "experiments": ["sandboxes"], "manifest": { "source": "local" }, "project_id": "..." }After:
{ "experiments": { "sandboxes": true, "charm" :false }, "manifest": { "source": "local" }, "project_id": "..." }Motivation
Why on earth are you doing this? 👨🏻🎨 🎨 🚲 🏠
The motivation is to unblock adding a
slack configcommand to get and set system-level and project-level config files.The config command will use a dot-notation to get and set key values:
When the
<value>is a simple type, it works well, such as string, number, or booleanWhen the
<value>is a complex type, it's not intuitive or simple, such as arraysIn order for setting
[value]to be simple and intuitive, we should restrict theconfig.jsonfiles to use simple value types that can be expressed on the command-line (e.g. string, number, and boolean). Arrays are complex and error-prone to express (["value1", "value2"]orvalue1, value2) and all elements must be included each time.This is why this pull request migrates the experiments to an object of
key: booleanpairs.An added bonus is that the new format naturally supports disabling experiments from the config file. 🧠
Requirements